package com.archgeek.rpc.proxy.custom;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author pizhihui
 * @date 2024-05-22 23:14
 */
public class MyProxy {


    private final static String RN = "\r\n";

    public static Object newProxyInstance(MyClassLoader classLoader,
                                          Class<?> interfaces,
                                          MyInvocationHandler h) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        String s = genSesource2(interfaces);
        String path = MyProxy.class.getResource("").getPath();
        File file = new File(path + "$Proxy0.java");
        FileWriter fileWriter = new FileWriter(file);
        fileWriter.write(s);
        fileWriter.close();


//        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
//        StandardJavaFileManager manager = javaCompiler.getStandardFileManager(null, null, null);
//        Iterable<? extends JavaFileObject> javaFileObjects = manager.getJavaFileObjects(file);
//        JavaCompiler.CompilationTask task = javaCompiler.getTask(null, manager, null, null, null, javaFileObjects);
//        task.call();
//        manager.close();


        // 获取java编译器
        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        // 标注java文件管理器，用来获取java字节码文件
        StandardJavaFileManager manager = javaCompiler.getStandardFileManager(null,null,null);
        Iterable iterable = manager.getJavaFileObjects(file);
        // 创建task，通过java字节码文件将类信息加载到JVM中
        JavaCompiler.CompilationTask task = javaCompiler.getTask(null,manager,null,null,null,iterable);
        // 开始执行task
        task.call();
        // 关闭管理器
        manager.close();

        Class<?> proxyClass = classLoader.findClass("$Proxy0");
        Constructor<?> constructor = proxyClass.getConstructor(MyInvocationHandler.class);

        return constructor.newInstance(h);
    }


    private static String genSource(Class<?> interfacez) {
        StringBuilder sb = new StringBuilder();
        sb.append("package com.archgeek.java.proxy.custom;").append(RN);
        sb.append("import java.lang.reflect.Method;").append(RN);

        sb.append("public class $Proxy0 implements ").append(interfacez.getName()).append("{").append(RN);
        sb.append("private MyInvocationHandler h;").append(RN);
        sb.append("public $Proxy0(MyInvocationHandler h){").append(RN);
        sb.append("this.h = h;").append(RN);
        sb.append("}").append(RN);

        Method[] methods = interfacez.getMethods();
        for (Method method : methods) {
            sb.append("public ").append(method.getReturnType().getName()).append(" ").append(method.getName()).append("(").append(method.getParameterTypes()[0].getName() + " request").append("){").append(RN)
                    .append("try {").append(RN)
                    .append("Method m = ").append(interfacez.getName()).append(".class.getMethod(\"").append(method.getName()).append("\");").append(RN)
                    .append("this.h.invoke(this, m , new Object[]{});").append(RN)
                    .append("} catch (Exception e){ ").append(RN)
                    .append("}").append(RN)
                    .append("return new ").append(method.getReturnType().getName()).append("();").append(RN)
                    .append("}").append(RN);

        }

        sb.append("}").append(RN);


        return sb.toString();
    }

    private final static String ln = "\r\n";

    private static String genSesource2(Class<?> interfaces){
        StringBuilder src = new StringBuilder();
        src.append("package com.archgeek.java.proxy.custom;").append(ln)
                .append("import java.lang.reflect.Method;").append(ln)
                .append("public class $Proxy0 implements ").append(interfaces.getName()).append("{").append(ln)
                .append("private MyInvocationHandler h;").append(ln)
                .append("public $Proxy0(MyInvocationHandler h){").append(ln)
                .append("this.h=h;").append(ln)
                .append("}").append(ln);

        for(Method method:interfaces.getMethods()){
            src.append("public ").append(method.getReturnType()).append(" ").append(method.getName()).append("() {").append(ln)
                    .append("try {").append(ln)
                    .append("Method m = ").append(interfaces.getName()).append(".class.getMethod(\"").append(method.getName()).append("\");").append(ln)
                    .append("this.h.invoke(this, m, new Object[]{});").append(ln)
                    .append("}catch (Throwable e){").append(ln)
                    .append("e.printStackTrace();").append(ln)
                    .append("}").append(ln)
                    .append("}").append(ln);
        }
        src.append("}");

        return src.toString();

    }

}
